#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <chrono>
#include <cstring>


namespace hello
{

template<typename T>
struct hasher {};

template<>
struct hasher<std::string>
{
    static constexpr uint64_t k0 = 0xc3a5c85c97cb3127ULL;
    static constexpr uint64_t k1 = 0xb492b66fbe98f273ULL;
    static constexpr uint64_t k2 = 0x9ae16a3b2f90404fULL;

    static inline uint64_t rotate(uint64_t x, int offset)
    {
        return (x >> offset) | (x << (64 - offset));
    }

    static inline uint64_t hash(const std::string& _str)
    {
        uint64_t _ans = 0;
        auto len = _str.size();
        const char* _ptr = _str.c_str();
        auto real_len = (len / 8) * 8;
        uint64_t _block = 0;
        for (auto i = 0; i < real_len; i += 8)
        {
            _block = 0;
            std::memcpy(&_block, _ptr + i, sizeof(uint64_t));
            _ans ^= rotate(_block, 13);
            _ans *= k0;
            _ans ^= rotate(_block, 23);
            _ans *= k1;
            _ans ^= (_ans >> 7);
            _ans *= k2;
        }
        auto remain_len = len - real_len;
        if (remain_len > 0)
        {
            _block = 0;
            std::memcpy(&_block, _ptr + real_len, sizeof(uint64_t));
            _block = (_block >> (64 - remain_len) << (64 - remain_len));
            _block |= (k0 >> remain_len);
            _ans ^= rotate(_block, 13);
            _ans *= k0;
            _ans ^= rotate(_block, 23);
            _ans *= k1;
            _ans ^= (_ans >> 7);
            _ans *= k2;
        }
        return _ans;
    }
};

}
